//////////////////////////////////////////////////////////////////////////
//
// This file is a part of UKROP project.
// Copyright (c) Vladimir Gumenuk
//
// PROJECT:         Xort Custom Debug Engine
// FILE:            PendingBreakpoint.h : Declaration of the CPendingBreakpoint
// AUTHOR:          Vladimir Gumenuk
// DATE:            Dec-2012 -- Jan-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#pragma once

typedef CComEnumWithCount< IEnumDebugBoundBreakpoints2, 
    &IID_IEnumDebugBoundBreakpoints2, IDebugBoundBreakpoint2*, _CopyInterface<IDebugBoundBreakpoint2> > CEnumDebugBoundBreakpoints; 

typedef CComEnumWithCount< IEnumDebugErrorBreakpoints2, 
    &IID_IEnumDebugErrorBreakpoints2, IDebugErrorBreakpoint2*, _CopyInterface<IDebugErrorBreakpoint2> > CEnumDebugErrorBreakpoints; 

//////////////////////////////////////////////////////////////////////////
// helper wrapper around BP_CONDITION
class CBPCondition : public BP_CONDITION
{
public:
    CBPCondition() {
        pThread        = NULL;
        styleCondition = BP_COND_NONE;  // means break when reached
        bstrContext    = NULL;          // document
        bstrCondition  = NULL;          // expression to evaluate
        nRadix         = 10;            // Radix to be used in evaluating any numerical information.
    }
    CBPCondition( BP_CONDITION& cpy ) {
        Copy( this, &cpy );
    }
    ~CBPCondition() {
        Clear();
    }
    void operator= ( BP_CONDITION& cpy ) {
        if( this == &cpy ) return;
        Clear();
        Copy( this, &cpy );
    }
    void Clear() {
        Clear( this );
    }

    static void Clear( BP_CONDITION *dst ) {
        SAFE_RELEASE( dst->pThread );
        SAFE_FREESTR( dst->bstrContext );
        SAFE_FREESTR( dst->bstrCondition );
        dst->nRadix = 10;
        dst->styleCondition = BP_COND_NONE;
    }

    static void Copy( BP_CONDITION *dst, BP_CONDITION *src ) {
        dst->pThread        = src->pThread;
        if( dst->pThread )    dst->pThread->AddRef();
        dst->styleCondition = src->styleCondition;
        dst->bstrContext    = src->bstrContext ? SysAllocString( src->bstrContext ) : NULL;
        dst->bstrCondition  = src->bstrCondition ? SysAllocString( src->bstrCondition ) : NULL;
        dst->nRadix         = src->nRadix;
    }
};

class CBPRequestInfo : public BP_REQUEST_INFO
{
public:
    CBPRequestInfo() {
        dwFields = 0;
        ZeroMemory( &guidLanguage, sizeof(GUID) );
        ZeroMemory( &bpLocation, sizeof(bpLocation) );
        pProgram = NULL;
        bstrProgramName = NULL;
        pThread = NULL;
        bstrThreadName = NULL;
        ZeroMemory( &bpCondition, sizeof(bpCondition) );
        ZeroMemory( &bpPassCount, sizeof(bpPassCount) );
        dwFlags = BP_FLAG_NONE;
    }
    ~CBPRequestInfo() {
        Clear();
    }
    void Clear() {
        dwFields = 0;
        dwFlags = BP_FLAG_NONE;
        ZeroMemory( &guidLanguage, sizeof(GUID) );
        SAFE_RELEASE( pProgram );
        SAFE_RELEASE( pThread );
        if( bpLocation.bpLocationType == BPLT_CODE_FILE_LINE ) {
            SAFE_RELEASE( bpLocation.bpLocation.bplocCodeFileLine.pDocPos );
            SAFE_FREESTR( bpLocation.bpLocation.bplocCodeFileLine.bstrContext );
        } else { // TODO: support the rest!
            ATLASSERT( 0 );
        }
        SAFE_FREESTR( bstrProgramName );
        SAFE_FREESTR( bstrThreadName );
        SAFE_RELEASE( bpCondition.pThread );
        SAFE_FREESTR( bpCondition.bstrCondition );
        SAFE_FREESTR( bpCondition.bstrContext );
    }

    void CopyTo( BP_REQUEST_INFO& dst ) {
        dst.dwFields = dwFields;
        dst.guidLanguage = guidLanguage;
        dst.bpLocation.bpLocationType = bpLocation.bpLocationType;
        if( bpLocation.bpLocationType == BPLT_CODE_FILE_LINE ) {
            dst.bpLocation.bpLocation.bplocCodeFileLine.bstrContext = 
                bpLocation.bpLocation.bplocCodeFileLine.bstrContext ? 
                SysAllocString( bpLocation.bpLocation.bplocCodeFileLine.bstrContext ) : NULL;

            dst.bpLocation.bpLocation.bplocCodeFileLine.pDocPos = bpLocation.bpLocation.bplocCodeFileLine.pDocPos;
            if( dst.bpLocation.bpLocation.bplocCodeFileLine.pDocPos )
                dst.bpLocation.bpLocation.bplocCodeFileLine.pDocPos->AddRef();
        } else {
            ATLASSERT( 0 );
        }
        dst.pProgram = pProgram;
        if( dst.pProgram ) dst.pProgram->AddRef();
        dst.bstrProgramName = bstrProgramName ? SysAllocString( bstrProgramName ) : NULL;
        dst.pThread = pThread;
        if( dst.pThread ) dst.pThread->AddRef();
        dst.bstrThreadName = bstrThreadName ? SysAllocString( bstrThreadName ) : NULL;
        CBPCondition::Copy( &dst.bpCondition, &bpCondition );
        dst.bpPassCount.dwPassCount = bpPassCount.dwPassCount;
        dst.bpPassCount.stylePassCount = bpPassCount.stylePassCount;
        dst.dwFlags = dwFlags;
    }
};

//////////////////////////////////////////////////////////////////////////
//
class CBPResolutionInfo : public BP_RESOLUTION_INFO
{
public:
    CBPResolutionInfo() {
        dwFields = BPT_NONE;
        ZeroMemory( &bpResLocation, sizeof(bpResLocation) );
        pProgram = NULL;
        pThread = NULL;
    }

    ~CBPResolutionInfo() {
        SAFE_RELEASE( pProgram );
        SAFE_RELEASE( pThread );
        switch( bpResLocation.bpType )
        {
        case BPT_CODE:
            SAFE_RELEASE( bpResLocation.bpResLocation.bpresCode.pCodeContext );
            break;
        case BPT_DATA:
            SAFE_FREESTR( bpResLocation.bpResLocation.bpresData.bstrDataExpr );
            SAFE_FREESTR( bpResLocation.bpResLocation.bpresData.bstrFunc );
            SAFE_FREESTR( bpResLocation.bpResLocation.bpresData.bstrImage );
            bpResLocation.bpResLocation.bpresData.dwFlags = 0;
            break;

        default:
            break;
        }
        bpResLocation.bpType = BPT_NONE;
    }

    void CopyTo( BP_RESOLUTION_INFO *dst ) {
        dst->dwFields = dwFields;
        dst->bpResLocation.bpType = bpResLocation.bpType;
        switch( bpResLocation.bpType )
        {
        case BPT_CODE:
            dst->bpResLocation.bpResLocation.bpresCode.pCodeContext = bpResLocation.bpResLocation.bpresCode.pCodeContext;
            if( dst->bpResLocation.bpResLocation.bpresCode.pCodeContext )
                dst->bpResLocation.bpResLocation.bpresCode.pCodeContext->AddRef();
            break;

        case BPT_DATA:
            dst->bpResLocation.bpResLocation.bpresData.bstrDataExpr = bpResLocation.bpResLocation.bpresData.bstrDataExpr ? 
                SysAllocString( bpResLocation.bpResLocation.bpresData.bstrDataExpr ) : NULL;
            dst->bpResLocation.bpResLocation.bpresData.bstrFunc = bpResLocation.bpResLocation.bpresData.bstrFunc ?
                SysAllocString( bpResLocation.bpResLocation.bpresData.bstrFunc ) : NULL;
            dst->bpResLocation.bpResLocation.bpresData.bstrImage = bpResLocation.bpResLocation.bpresData.bstrImage ?
                SysAllocString( bpResLocation.bpResLocation.bpresData.bstrImage ) : NULL;
            dst->bpResLocation.bpResLocation.bpresData.dwFlags = bpResLocation.bpResLocation.bpresData.dwFlags;
            break;

        default:
            break;
        }

        dst->pProgram = pProgram;
        if( dst->pProgram ) dst->pProgram->AddRef();
        dst->pThread = pThread;
        if( dst->pThread ) dst->pThread->AddRef();
    }
};

//////////////////////////////////////////////////////////////////////////
// CPendingBreakpoint
class ATL_NO_VTABLE CPendingBreakpoint :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CPendingBreakpoint, &CLSID_PendingBreakpoint>,
	public IDebugPendingBreakpoint2,
    public IDebugBreakpointRequest2,
    public IXBreakpoint
{
public:
	CPendingBreakpoint();
    virtual ~CPendingBreakpoint();
	
DECLARE_REGISTRY_RESOURCEID(IDR_PENDINGBREAKPOINT)

BEGIN_COM_MAP(CPendingBreakpoint)
	COM_INTERFACE_ENTRY(IDebugPendingBreakpoint2)
    COM_INTERFACE_ENTRY(IDebugBreakpointRequest2)
    COM_INTERFACE_ENTRY(IXBreakpoint)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct();
	void FinalRelease();

    HRESULT Initialize( IDebugBreakpointRequest2 *pBPRequest );

    //////////////////////////////////////////////////////////////////////////
    // IXBreakpoint
    STDMETHOD(Initialize)( IDebugPendingBreakpoint2 *pPending, LPCWSTR pszFileName, TEXT_POSITION *pPos ) { return E_NOTIMPL; }
    STDMETHOD(SetIP)( DWORD dwIP ) { return E_NOTIMPL; }
    STDMETHOD(GetIP)( DWORD *pdwIP ) { return E_NOTIMPL; }
    STDMETHOD(GetPosition)( TEXT_POSITION *pPos ) { *pPos = m_BPPos; return S_OK; }
    STDMETHOD(GetSymbol)( DWORD **ppOut ) { *ppOut = (DWORD*)m_pSymProc; return S_OK; }
    STDMETHOD(BindBP)( IDebugBoundBreakpoint2 *pBP );
    STDMETHOD(UnbindBP)( IDebugBoundBreakpoint2 *pBP );
    STDMETHOD(IncrementHitCounter)() { return E_NOTIMPL; }

    //////////////////////////////////////////////////////////// 
    // IDebugPendingBreakpoint2 
    STDMETHOD(CanBind)( IEnumDebugErrorBreakpoints2** ppErrorEnum ); 
    STDMETHOD(Bind)(); 
    STDMETHOD(GetState)( PENDING_BP_STATE_INFO* pState ); 
    STDMETHOD(GetBreakpointRequest)( IDebugBreakpointRequest2** ppBPRequest ); 
    STDMETHOD(Virtualize)( BOOL fVirtualize ); 
    STDMETHOD(Enable)( BOOL fEnable ); 
    STDMETHOD(SetCondition)( BP_CONDITION bpCondition ); 
    STDMETHOD(SetPassCount)( BP_PASSCOUNT bpPassCount ); 
    STDMETHOD(EnumBoundBreakpoints)( IEnumDebugBoundBreakpoints2** ppEnum ); 
    STDMETHOD(EnumErrorBreakpoints)( BP_ERROR_TYPE bpErrorType, IEnumDebugErrorBreakpoints2** ppEnum ); 
    STDMETHOD(Delete)(); 

    //////////////////////////////////////////////////////////////////////////
    // IDebugBreakpointRequest2
    STDMETHOD(GetLocationType)( BP_LOCATION_TYPE *pBPLocationType );
    STDMETHOD(GetRequestInfo)( BPREQI_FIELDS dwFields, BP_REQUEST_INFO *pBPRequestInfo );

private:
    void SendBoundEvent( IDebugBoundBreakpoint2 *pBoundBP );

    BP_LOCATION_TYPE                    m_LocationType;
    CComPtr<IDebugBoundBreakpoint2>     m_spBoundBP;    // bound BP created from this pending BP
    CComBSTR                            m_bsDoc;        // document name
    TEXT_POSITION                       m_BPPos;        // position in src file, engine can update it to the nearest valid lines!
    PENDING_BP_STATE_INFO               m_bpState;
    CBPCondition                        m_Condition;
    BP_PASSCOUNT                        m_PassCount;
    CBPRequestInfo                      m_BPRequest;    // request that created this pending bp
    Symbol                             *m_pSymProc;     // pointer to Xort procedure symbol that will hold future bound BP
    DWORD                               m_dwInstrIP;
};

OBJECT_ENTRY_AUTO(__uuidof(PendingBreakpoint), CPendingBreakpoint)

//////////////////////////////////////////////////////////////////////////
// CBoundBreakpoint
class ATL_NO_VTABLE CBoundBreakpoint :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CBoundBreakpoint, &CLSID_BoundBreakpoint>,
    public IDebugBoundBreakpoint2,
    public IDebugBreakpointResolution2,
    public IXBreakpoint
{
public:
    CBoundBreakpoint();
    virtual ~CBoundBreakpoint();

DECLARE_REGISTRY_RESOURCEID(IDR_PENDINGBREAKPOINT)

BEGIN_COM_MAP(CBoundBreakpoint)
    COM_INTERFACE_ENTRY(IDebugBoundBreakpoint2)
    COM_INTERFACE_ENTRY(IDebugBreakpointResolution2)
    COM_INTERFACE_ENTRY(IXBreakpoint)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct();
    void FinalRelease();
    
public:
    STDMETHOD(Initialize)( IDebugPendingBreakpoint2 *pPending, LPCWSTR pszFileName, TEXT_POSITION *pPos );
    STDMETHOD(SetIP)( DWORD dwIP );
    STDMETHOD(GetIP)( DWORD *pdwIP );
    STDMETHOD(GetPosition)( TEXT_POSITION *pPos );
    STDMETHOD(GetSymbol)( DWORD **ppOut );
    STDMETHOD(BindBP)( IDebugBoundBreakpoint2 *pBP ) { return E_NOTIMPL; }
    STDMETHOD(UnbindBP)( IDebugBoundBreakpoint2 *pBP ) { return E_NOTIMPL; }
    STDMETHOD(IncrementHitCounter)();

    //////////////////////////////////////////////////////////// 
    // IDebugBoundBreakpoint2 
    STDMETHOD(GetPendingBreakpoint)( IDebugPendingBreakpoint2** ppPendingBreakpoint );
    STDMETHOD(GetState)( BP_STATE* pState );  
    STDMETHOD(GetHitCount)( DWORD* pdwHitCount );  
    STDMETHOD(GetBreakpointResolution)( IDebugBreakpointResolution2** ppBPResolution );
    STDMETHOD(Enable)( BOOL fEnable );  
    STDMETHOD(SetHitCount)(DWORD dwHitCount);  
    STDMETHOD(SetCondition)( BP_CONDITION bpCondition );  
    STDMETHOD(SetPassCount)( BP_PASSCOUNT bpPassCount );
    STDMETHOD(Delete)(); 

    //////////////////////////////////////////////////////////// 
    // IDebugBreakpointResolution2 

    // BPT_NONE, BPT_CODE, BPT_DATA, BPT_SPECIAL
    STDMETHOD(GetBreakpointType)( BP_TYPE* pBPType );

    // dwFields: BPRESI_BPRESLOCATION, BPRESI_PROGRAM, BPRESI_THREAD, BPRESI_ALLFIELDS
    STDMETHOD(GetResolutionInfo)( BPRESI_FIELDS dwFields, BP_RESOLUTION_INFO* pBPResolutionInfo );

private:
    CComPtr<IDebugPendingBreakpoint2>       m_spPendingBP;
    CBPResolutionInfo                       m_bpResolutionInfo;
    TEXT_POSITION                           m_BPPos;
    BP_STATE                                m_bpState;
    DWORD                                   m_dwIP;         // internal bp idx in the program object
    DWORD                                   m_dwHitCount;
    CBPCondition                            m_Condition;    // condition to fire bp
    BP_PASSCOUNT                            m_PassCount;    // counter of passes through this bp before it fires
};

OBJECT_ENTRY_AUTO(__uuidof(BoundBreakpoint), CBoundBreakpoint)



